﻿/*!
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

using System;

namespace Pittypat
{
    /// <summary>
    /// 表示一个时间间隔。
    /// </summary>
    public struct @interval : IPrimitive
    {
        /// <summary>
        /// 将指定的字符串解析为一个 @interval 值。
        /// </summary>
        /// <param name="s">要解析的字符串。</param>
        /// <param name="provider">提供有关 s 的区域性特定格式信息。</param>
        /// <returns>一个 @interval 或 null。</returns>
        public static @interval? Parse(string s, IFormatProvider provider = null)
        {
            if (String.IsNullOrEmpty(s))
            {
                return null;
            }

            s = s.Trim();
            if (String.IsNullOrEmpty(s))
            {
                return null;
            }

            switch (s.ToLower())
            {
                case "min":
                case "minvalue":
                case "min_value":
                    return TimeSpan.MinValue;
                case "max":
                case "maxvalue":
                case "max_value":
                    return TimeSpan.MaxValue;
                case "zero":
                case "default":
                    return TimeSpan.Zero;
                default:
                    break;
            }

            TimeSpan value = TimeSpan.Zero;
            if (provider != null)
            {
                if (TimeSpan.TryParse(s, provider, out value))
                {
                    return value;
                }
            }
            else
            {
                if (TimeSpan.TryParse(s, out value))
                {
                    return value;
                }
            }

            return null;
        }

        /// <summary>
        /// 从天数创建 @interval。
        /// </summary>
        /// <param name="days">天数。</param>
        /// <returns>一个 @interval 值。</returns>
        public static @interval FromDays(double days)
        {
            return TimeSpan.FromDays(days);
        }

        /// <summary>
        /// 从小时数创建 @interval。
        /// </summary>
        /// <param name="hours">小时数。</param>
        /// <returns>一个 @interval 值。</returns>
        public static @interval FromHours(double hours)
        {
            return TimeSpan.FromHours(hours);
        }

        /// <summary>
        /// 从分钟数创建 @interval。
        /// </summary>
        /// <param name="minutes">分钟数。</param>
        /// <returns>一个 @interval 值。</returns>
        public static @interval FromMinutes(double minutes)
        {
            return TimeSpan.FromMinutes(minutes);
        }

        /// <summary>
        /// 从秒数创建 @interval。
        /// </summary>
        /// <param name="seconds">秒数。</param>
        /// <returns>一个 @interval 值。</returns>
        public static @interval FromSeconds(double seconds)
        {
            return TimeSpan.FromSeconds(seconds);
        }

        /// <summary>
        /// 从毫秒数创建 @interval。
        /// </summary>
        /// <param name="milliseconds">毫秒数。</param>
        /// <returns>一个 @interval 值。</returns>
        public static @interval FromMilliseconds(double milliseconds)
        {
            return TimeSpan.FromMilliseconds(milliseconds);
        }

        /// <summary>
        /// 从刻度数创建 @interval。
        /// </summary>
        /// <param name="ticks">刻度数。</param>
        /// <returns>一个 @interval 值。</returns>
        public static @interval FromMilliseconds(long ticks)
        {
            return TimeSpan.FromTicks(ticks);
        }

        /// <summary>
        /// 计算两个时间间隔的和。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>两个值的和。</returns>
        public static @interval operator +(@interval x, @interval y)
        {
            return new @interval(x.value.Add(y.value));
        }

        /// <summary>
        /// 计算两个时间间隔的差。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>两个值的差。</returns>
        public static @interval operator -(@interval x, @interval y)
        {
            return new @interval(x.value.Subtract(y.value));
        }

        /// <summary>
        /// 比较两个值的大小。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果 x 大于 y，则返回 true，否则返回 false。</returns>
        public static bool operator >(@interval x, @interval y)
        {
            return x.value > y.value;
        }

        /// <summary>
        /// 比较两个值的大小。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果 x 大于等于 y，则返回 true，否则返回 false。</returns>
        public static bool operator >=(@interval x, @interval y)
        {
            return x.value >= y.value;
        }

        /// <summary>
        /// 比较两个值的大小。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果 x 小于 y，则返回 true，否则返回 false。</returns>
        public static bool operator <(@interval x, @interval y)
        {
            return x.value < y.value;
        }

        /// <summary>
        /// 比较两个值的大小。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果 x 小于等于 y，则返回 true，否则返回 false。</returns>
        public static bool operator <=(@interval x, @interval y)
        {
            return x.value <= y.value;
        }

        /// <summary>
        /// 表示 @interval 的最小值。
        /// </summary>
        public static readonly @interval MinValue = TimeSpan.MinValue;

        /// <summary>
        /// 表示 @interval 的最大值。
        /// </summary>
        public static readonly @interval MaxValue = TimeSpan.MaxValue;

        /// <summary>
        /// 表示 @interval 的 0 值。
        /// </summary>
        public static readonly @interval Zero = TimeSpan.Zero;

        private TimeSpan value;

        /// <summary>
        /// 初始化 @interval 结构的新实例。
        /// </summary>
        /// <param name="value">结构的值。</param>
        public @interval(TimeSpan value)
        {
            this.value = value;
        }

        /// <summary>
        /// 初始化 @interval 结构的新实例。
        /// </summary>
        /// <param name="value">结构的值。</param>
        public @interval(long value)
        {
            this.value = TimeSpan.FromTicks(value);
        }

        /// <summary>
        /// 初始化 @interval 结构的新实例。
        /// </summary>
        /// <param name="days">天数。</param>
        /// <param name="hours">小时数。</param>
        /// <param name="mintues">分钟数。</param>
        /// <param name="seconds">秒数。</param>
        /// <param name="milliseconds">毫秒数。</param>
        public @interval(int days, int hours, int mintues, int seconds, int milliseconds)
        {
            this.value = new TimeSpan(days, hours, mintues, seconds, milliseconds);
        }

        /// <summary>
        /// 获取天数。
        /// </summary>
        public int Days
        {
            get
            {
                return this.value.Days;
            }
        }

        /// <summary>
        /// 获取小时数。
        /// </summary>
        public int Hours
        {
            get
            {
                return this.value.Hours;
            }
        }

        /// <summary>
        /// 获取分钟数。
        /// </summary>
        public int Minutes
        {
            get
            {
                return this.value.Minutes;
            }
        }

        /// <summary>
        /// 获取秒数。
        /// </summary>
        public int Seconds
        {
            get
            {
                return this.value.Seconds;
            }
        }

        /// <summary>
        /// 获取毫秒数。
        /// </summary>
        public int Milliseconds
        {
            get
            {
                return this.value.Milliseconds;
            }
        }

        /// <summary>
        /// 获取此时间间隔的刻度数。
        /// </summary>
        public long Ticks
        {
            get
            {
                return this.value.Ticks;
            }
        }

        /// <summary>
        /// 获取以整天数和天的小数部分表示的当前值。
        /// </summary>
        public double TotalDays
        {
            get
            {
                return this.value.TotalDays;
            }
        }

        /// <summary>
        /// 获取以整小时数和小时的小数部分表示的当前值。
        /// </summary>
        public double TotalHours
        {
            get
            {
                return this.value.TotalHours;
            }
        }

        /// <summary>
        /// 获取以整分钟数和分钟的小数部分表示的当前值。
        /// </summary>
        public double TotalMinutes
        {
            get
            {
                return this.value.TotalMinutes;
            }
        }

        /// <summary>
        /// 获取以整秒数和秒的小数部分表示的当前值。
        /// </summary>
        public double TotalSeconds
        {
            get
            {
                return this.value.TotalSeconds;
            }
        }

        /// <summary>
        /// 获取以整毫秒数和毫秒的小数部分表示的当前值。
        /// </summary>
        public double TotalMilliseconds
        {
            get
            {
                return this.value.TotalMilliseconds;
            }
        }

        /// <summary>
        /// 获取当前时间间隔的绝对值。
        /// </summary>
        public @interval Duration
        {
            get
            {
                return this.value.Duration();
            }
        }

        /// <summary>
        /// 返回其值为此实例的相反值的新时间间隔。
        /// </summary>
        public @interval Negation
        {
            get
            {
                return this.value.Negate();
            }
        }

        /// <summary>
        /// 计算两个时间间隔的和。
        /// </summary>
        /// <param name="value">要添加的时间间隔。</param>
        /// <returns>两个值的和。</returns>
        public @interval Add(@interval value)
        {
            return new interval(this.value.Add(value.value));
        }

        /// <summary>
        /// 计算两个时间间隔的差。
        /// </summary>
        /// <param name="value">要减去的时间间隔。</param>
        /// <returns>两个值的差。</returns>
        public @interval Subtract(@interval value)
        {
            return new interval(this.value.Subtract(value.value));
        }

        /// <summary>
        /// 将当前值转换为 int 类型的值。
        /// </summary>
        /// <param name="value">要转换的值。</param>
        /// <returns>转换后的值。</returns>
        public static implicit operator TimeSpan(@interval value)
        {
            return (TimeSpan)value.value;
        }

        /// <summary>
        /// 将指定的值转换为 @interval 类型的值。
        /// </summary>
        /// <param name="value">要转换的值。</param>
        /// <returns>转换后的值。</returns>
        public static implicit operator @interval(TimeSpan value)
        {
            return new @interval(value);
        }

        /// <summary>
        /// 将指定的值转换为 @interval 类型的值。
        /// </summary>
        /// <param name="value">要转换的值。</param>
        /// <returns>转换后的值。</returns>
        public static implicit operator @interval(long value)
        {
            return new @interval(value);
        }

        /// <summary>
        /// 将指定的值转换为 @interval 类型的值。
        /// </summary>
        /// <param name="value">要转换的值。</param>
        /// <returns>转换后的值。</returns>
        public static implicit operator @interval(string value)
        {
            return Parse(value) ?? Zero;
        }

        internal static @interval? Parse(string value)
        {
            if (String.IsNullOrEmpty(value))
            {
                return null;
            }

            value = value.ToLower().Trim();
            if (value == "0")
            {
                return Zero;
            }

            if (value == "min")
            {
                return MinValue;
            }

            if (value == "max")
            {
                return MaxValue;
            }

            try
            {
                return String.IsNullOrEmpty(value) ? (@interval?)null : (@interval?)new @interval(TimeSpan.Parse(value));
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 比较两个值是否相等。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果两个值相等，则返回 true，否则返回 false。</returns>
        public static bool operator ==(@interval x, @interval y)
        {
            return x.value == y.value;
        }

        /// <summary>
        /// 比较两个值是否不相等。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果两个值不相等，则返回 true，否则返回 false。</returns>
        public static bool operator !=(@interval x, @interval y)
        {
            return x.value != y.value;
        }

        /// <summary>
        /// 判断两个值是否相等。
        /// </summary>
        /// <param name="obj">要比较的值。</param>
        /// <returns>如果两个值相等，则返回 true，否则返回 false。</returns>
        public override bool Equals(object obj)
        {
            if (obj is @interval)
            {
                return this.value == ((@interval)obj).value;
            }

            if (obj is TimeSpan)
            {
                return this.value == (TimeSpan)obj;
            }

            return false;
        }

        /// <summary>
        /// 返回此值的哈希代码。
        /// </summary>
        /// <returns>此值的哈希代码。</returns>
        public override int GetHashCode()
        {
            return this.value.GetHashCode();
        }

        /// <summary>
        /// 返回此值的字符串表示形式。
        /// </summary>
        /// <returns>此值的字符串表示形式。</returns>
        public override string ToString()
        {
            return this.ToString(null);
        }

        #region IPrimitive Members

        object IPrimitive.Value
        {
            get
            {
                return this.value.Ticks;
            }
        }

        /// <summary>
        /// 将当前对象作为 JSON 字符串写入指定的文本流。
        /// </summary>
        /// <param name="writer">目标文本流。</param>
        /// <exception cref="ArgumentNullException">writer 是 null。</exception>
        public void Write(System.IO.TextWriter writer)
        {
            if (writer == null)
            {
                throw new ArgumentNullException("writer");
            }

            writer.Write("{{\"days\":{0},\"hours\":{1},\"minutes\":{2},\"seconds\":{3},\"milliseconds\":{4}}}",
                this.value.Days, this.value.Hours, this.value.Minutes, this.value.Seconds, this.value.Milliseconds);
        }

        #endregion

        #region IConvertible Members

        TypeCode IConvertible.GetTypeCode()
        {
            return TypeCode.Object;
        }

        bool IConvertible.ToBoolean(IFormatProvider provider)
        {
            return this.value != TimeSpan.Zero;
        }

        byte IConvertible.ToByte(IFormatProvider provider)
        {
            return unchecked((byte)this.value.Ticks);
        }

        char IConvertible.ToChar(IFormatProvider provider)
        {
            return unchecked((char)this.value.Ticks);
        }

        DateTime IConvertible.ToDateTime(IFormatProvider provider)
        {
            return new DateTime(this.value.Ticks);
        }

        decimal IConvertible.ToDecimal(IFormatProvider provider)
        {
            return unchecked((decimal)this.value.Ticks);
        }

        double IConvertible.ToDouble(IFormatProvider provider)
        {
            return unchecked((double)this.value.Ticks);
        }

        short IConvertible.ToInt16(IFormatProvider provider)
        {
            return unchecked((short)this.value.Ticks);
        }

        int IConvertible.ToInt32(IFormatProvider provider)
        {
            return unchecked((int)this.value.Ticks);
        }

        long IConvertible.ToInt64(IFormatProvider provider)
        {
            return unchecked((long)this.value.Ticks);
        }

        sbyte IConvertible.ToSByte(IFormatProvider provider)
        {
            return unchecked((sbyte)this.value.Ticks);
        }

        float IConvertible.ToSingle(IFormatProvider provider)
        {
            return unchecked((float)this.value.Ticks);
        }

        string IConvertible.ToString(IFormatProvider provider)
        {
            return this.value.ToString();
        }

        object IConvertible.ToType(Type conversionType, IFormatProvider provider)
        {
            return ((IConvertible)this.value.Ticks).ToType(conversionType, provider);
        }

        ushort IConvertible.ToUInt16(IFormatProvider provider)
        {
            return unchecked((ushort)this.value.Ticks);
        }

        uint IConvertible.ToUInt32(IFormatProvider provider)
        {
            return unchecked((uint)this.value.Ticks);
        }

        ulong IConvertible.ToUInt64(IFormatProvider provider)
        {
            return unchecked((ulong)this.value.Ticks);
        }

        #endregion

        #region IComparable Members

        int IComparable.CompareTo(object other)
        {
            if (other is @interval)
            {
                return this.value.CompareTo(((@interval)other).value);
            }

            return 1;
        }

        #endregion

        #region IFormattable Members

        /// <summary>
        /// 使用指定的格式格式化当前实例的值。
        /// </summary>
        /// <param name="format">指定要使用的格式的 String。</param>
        /// <param name="formatProvider">用于格式化该值的 IFormatProvider。null 表示从操作系统的当前区域设置中获取数字格式信息。</param>
        /// <returns>一个 String，包含具有指定格式的当前实例的值。</returns>
        public string ToString(string format, IFormatProvider formatProvider)
        {
            return this.value.ToString(format ?? "c", formatProvider);
        }

        /// <summary>
        /// 使用指定的格式格式化当前实例的值。
        /// </summary>
        /// <param name="format">指定要使用的格式的 String。</param>
        /// <returns>一个 String，包含具有指定格式的当前实例的值。</returns>
        public string ToString(string format)
        {
            return this.ToString(format, null);
        }

        #endregion
    }
}
